#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include "imglib.h"
#define R 0
#define G 1
#define B 2
#define P2(x) ((x)*(x))
void color_inversion(image image_A,image image_B)
{
    int i,j;
    int width=image_A->width;
    int height=image_A->height;
    for(i=0;i<height;i++)
    {
        for(j=0;j<width;j++)
        {
            int ofs=i*width+j;
            image_B->buf[ofs][R]=image_A->buf[ofs][G];
            image_B->buf[ofs][G]=image_A->buf[ofs][R];
            image_B->buf[ofs][B]=image_A->buf[ofs][B];
        }
    }
}

void purifier(image image_A,image image_B)
{

    int i,j;
    int width=image_A->width;
    int height=image_A->height;
    for(i=0;i<height;i++)
    {
        for(j=0;j<width;j++)
        {
            int ofs=i*width+j;

            int r = image_A->buf[ofs][R], g=image_A->buf[ofs][G], b=image_A->buf[ofs][B];

			if( P2(r-g) + P2(r-b) + P2(g-b) > 3*P2(30) ) {
				// pure enough!
				image_B->buf[ofs][R]=r;
                image_B->buf[ofs][G]=g;
                image_B->buf[ofs][B]=b;
			}
			else {
				int avg = (( r + g + b ) / 3.0);

				image_B->buf[ofs][R]=avg;
                image_B->buf[ofs][G]=avg;
                image_B->buf[ofs][B]=avg;
			}

        }
    }
}

void edgemaker(image image_A,image image_B)
{
    #define EDGE_THRESHOLD 30
    int i,j;
    int width=image_A->width;
    int height=image_A->height;
    /*
        For all the inner points (not on the border) if the difference between them and their
        neighbors is greater than a given threshold, paint them white*/
    for(i=1;i<height-1;i++)
    {
        for(j=1;j<width-1;j++)
        {
            int ofs=i*width+j;
            //get r g b components
            int r = image_A->buf[ofs][R], g=image_A->buf[ofs][G], b=image_A->buf[ofs][B];
            int diff=0;

            //compute the diff between neighbors
            diff+=abs(r-image_A->buf[ofs-1][R])+abs(r-image_A->buf[ofs+1][R])+abs(r-image_A->buf[ofs-width][R])+abs(r-image_A->buf[ofs+width][R]);
            diff+=abs(g-image_A->buf[ofs-1][G])+abs(g-image_A->buf[ofs+1][G])+abs(g-image_A->buf[ofs-width][G])+abs(g-image_A->buf[ofs+width][G]);
            diff+=abs(b-image_A->buf[ofs-1][B])+abs(b-image_A->buf[ofs+1][B])+abs(b-image_A->buf[ofs-width][B])+abs(b-image_A->buf[ofs+width][B]);

            if(diff>EDGE_THRESHOLD)
            {
                image_B->buf[ofs][R]=r;
                image_B->buf[ofs][G]=g;
                image_B->buf[ofs][B]=b;
            }
            else
            {
                image_B->buf[ofs][R]=255;
                image_B->buf[ofs][G]=255;
                image_B->buf[ofs][B]=255;
            }

        }
    }
}

void blur(image image_A,image image_B)
{
    #define RADIUS 1
    float gauss[3][3]={{0.050,0.125,0.050},{0.125,0.3,0.125},{0.050,0.125,0.050}};
    int i,j;
    int width=image_A->width;
    int height=image_A->height;
    /*
        For all the inner points (not on the border) if the difference between them and their
        neighbors is greater than a given threshold, paint them white*/
    for(i=1;i<height-1;i++)
    {
        for(j=1;j<width-1;j++)
        {
            int ofs=i*width+j;
            //get r g b components
            int r = image_A->buf[ofs][R], g=image_A->buf[ofs][G], b=image_A->buf[ofs][B];
            int diff=0;
            int kx,ky;
            double nr=0,ng=0,nb=0;
            //consider the neighbor within a certain radius
            for( ky = -RADIUS; ky <= RADIUS; ky++)
            {
                for( kx = -RADIUS; kx <= RADIUS; kx++)
                    {
                        //each pixel position
                        int off=(i+ky)*width+(j+kx);

                        nr +=  ((double)(image_A->buf[off][R]))*gauss[ky+RADIUS][kx+RADIUS];
                       // printf("%d %d %d %f %f\n",kx,ky, image_A->buf[off][R],gauss[ky+RADIUS][kx+RADIUS],nr);
                        ng +=  image_A->buf[off][G]*gauss[ky+RADIUS][kx+RADIUS];
                        nb +=  image_A->buf[off][B]*gauss[ky+RADIUS][kx+RADIUS];
                    }//end for #3
                }//end for #4

               /* nr += ((RADIUS *2 + 1)*(RADIUS *2 + 1));
                ng += ((RADIUS *2 + 1)*(RADIUS *2 + 1));
                nb += ((RADIUS *2 + 1)*(RADIUS *2 + 1));*/
                //set the output
                image_B->buf[ofs][R]=nr;
                image_B->buf[ofs][G]=ng;
                image_B->buf[ofs][B]=nb;
        }
    }
}
int main(int argc, char *argv[])
{

    image image_A=read_image(argv[1]);
    image image_B=alloc_img(image_A->width, image_A->height);

    struct timeval tmp_t;
    long start_tmill=0, end_tmill=0,tmp_mill;
    gettimeofday(&tmp_t,NULL);
    start_tmill=tmp_t.tv_sec*1000+(tmp_t.tv_usec/1000);
    tmp_mill=tmp_t.tv_sec*1000+(tmp_t.tv_usec/1000);
    color_inversion(image_A,image_B);
    gettimeofday(&tmp_t,NULL);
    end_tmill=tmp_t.tv_sec*1000+(tmp_t.tv_usec/1000)-tmp_mill;
    printf("Inversion Time elapsed (msec): %ld\n",end_tmill);

    gettimeofday(&tmp_t,NULL);
    tmp_mill=tmp_t.tv_sec*1000+(tmp_t.tv_usec/1000);
    purifier(image_B,image_A);
    gettimeofday(&tmp_t,NULL);
    end_tmill=tmp_t.tv_sec*1000+(tmp_t.tv_usec/1000)-tmp_mill;
    printf("Purifier Time elapsed (msec): %ld\n",end_tmill);

    gettimeofday(&tmp_t,NULL);
    tmp_mill=tmp_t.tv_sec*1000+(tmp_t.tv_usec/1000);
    blur(image_A,image_B);
    gettimeofday(&tmp_t,NULL);
    end_tmill=tmp_t.tv_sec*1000+(tmp_t.tv_usec/1000)-tmp_mill;
    printf("Blur Time elapsed (msec): %ld\n",end_tmill);

    gettimeofday(&tmp_t,NULL);
    tmp_mill=tmp_t.tv_sec*1000+(tmp_t.tv_usec/1000);
    edgemaker(image_B,image_A);
    gettimeofday(&tmp_t,NULL);
    end_tmill=tmp_t.tv_sec*1000+(tmp_t.tv_usec/1000)-tmp_mill;
    printf("Edgemaker Time elapsed (msec): %ld\n",end_tmill);
    end_tmill=tmp_t.tv_sec*1000+(tmp_t.tv_usec/1000)-start_tmill;
    printf("Total Time elapsed (msec): %ld\n",end_tmill);
    FILE *fout=fopen("images/output.ppm","w");
    output_ppm(fout,image_A);
    fclose(fout);
    return 0;


}
